1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkNotNull;
20  
21  import com.google.common.annotations.GwtCompatible;
22  
23  import java.util.Collection;
24  import java.util.List;
25  import java.util.ListIterator;
26  import java.util.RandomAccess;
27  import java.util.Set;
28  import java.util.SortedSet;
29  
30  /**
31   * Factories and utilities pertaining to the {@link Constraint} interface.
32   *
33   * @author Mike Bostock
34   * @author Jared Levy
35   */
36  @GwtCompatible
37  final class Constraints {
38    private Constraints() {}
39  
40    /**
41     * Returns a constrained view of the specified collection, using the specified
42     * constraint. Any operations that add new elements to the collection will
43     * call the provided constraint. However, this method does not verify that
44     * existing elements satisfy the constraint.
45     *
46     * <p>The returned collection is not serializable.
47     *
48     * @param collection the collection to constrain
49     * @param constraint the constraint that validates added elements
50     * @return a constrained view of the collection
51     */
52    public static <E> Collection<E> constrainedCollection(
53        Collection<E> collection, Constraint<? super E> constraint) {
54      return new ConstrainedCollection<E>(collection, constraint);
55    }
56  
57    /** @see Constraints#constrainedCollection */
58    static class ConstrainedCollection<E> extends ForwardingCollection<E> {
59      private final Collection<E> delegate;
60      private final Constraint<? super E> constraint;
61  
62      public ConstrainedCollection(
63          Collection<E> delegate, Constraint<? super E> constraint) {
64        this.delegate = checkNotNull(delegate);
65        this.constraint = checkNotNull(constraint);
66      }
67      @Override protected Collection<E> delegate() {
68        return delegate;
69      }
70      @Override public boolean add(E element) {
71        constraint.checkElement(element);
72        return delegate.add(element);
73      }
74      @Override public boolean addAll(Collection<? extends E> elements) {
75        return delegate.addAll(checkElements(elements, constraint));
76      }
77    }
78  
79    /**
80     * Returns a constrained view of the specified set, using the specified
81     * constraint. Any operations that add new elements to the set will call the
82     * provided constraint. However, this method does not verify that existing
83     * elements satisfy the constraint.
84     *
85     * <p>The returned set is not serializable.
86     *
87     * @param set the set to constrain
88     * @param constraint the constraint that validates added elements
89     * @return a constrained view of the set
90     */
91    public static <E> Set<E> constrainedSet(
92        Set<E> set, Constraint<? super E> constraint) {
93      return new ConstrainedSet<E>(set, constraint);
94    }
95  
96    /** @see Constraints#constrainedSet */
97    static class ConstrainedSet<E> extends ForwardingSet<E> {
98      private final Set<E> delegate;
99      private final Constraint<? super E> constraint;
100 
101     public ConstrainedSet(Set<E> delegate, Constraint<? super E> constraint) {
102       this.delegate = checkNotNull(delegate);
103       this.constraint = checkNotNull(constraint);
104     }
105     @Override protected Set<E> delegate() {
106       return delegate;
107     }
108     @Override public boolean add(E element) {
109       constraint.checkElement(element);
110       return delegate.add(element);
111     }
112     @Override public boolean addAll(Collection<? extends E> elements) {
113       return delegate.addAll(checkElements(elements, constraint));
114     }
115   }
116 
117   /**
118    * Returns a constrained view of the specified sorted set, using the specified
119    * constraint. Any operations that add new elements to the sorted set will
120    * call the provided constraint. However, this method does not verify that
121    * existing elements satisfy the constraint.
122    *
123    * <p>The returned set is not serializable.
124    *
125    * @param sortedSet the sorted set to constrain
126    * @param constraint the constraint that validates added elements
127    * @return a constrained view of the sorted set
128    */
129   public static <E> SortedSet<E> constrainedSortedSet(
130       SortedSet<E> sortedSet, Constraint<? super E> constraint) {
131     return new ConstrainedSortedSet<E>(sortedSet, constraint);
132   }
133 
134   /** @see Constraints#constrainedSortedSet */
135   private static class ConstrainedSortedSet<E> extends ForwardingSortedSet<E> {
136     final SortedSet<E> delegate;
137     final Constraint<? super E> constraint;
138 
139     ConstrainedSortedSet(
140         SortedSet<E> delegate, Constraint<? super E> constraint) {
141       this.delegate = checkNotNull(delegate);
142       this.constraint = checkNotNull(constraint);
143     }
144     @Override protected SortedSet<E> delegate() {
145       return delegate;
146     }
147     @Override public SortedSet<E> headSet(E toElement) {
148       return constrainedSortedSet(delegate.headSet(toElement), constraint);
149     }
150     @Override public SortedSet<E> subSet(E fromElement, E toElement) {
151       return constrainedSortedSet(
152           delegate.subSet(fromElement, toElement), constraint);
153     }
154     @Override public SortedSet<E> tailSet(E fromElement) {
155       return constrainedSortedSet(delegate.tailSet(fromElement), constraint);
156     }
157     @Override public boolean add(E element) {
158       constraint.checkElement(element);
159       return delegate.add(element);
160     }
161     @Override public boolean addAll(Collection<? extends E> elements) {
162       return delegate.addAll(checkElements(elements, constraint));
163     }
164   }
165 
166   /**
167    * Returns a constrained view of the specified list, using the specified
168    * constraint. Any operations that add new elements to the list will call the
169    * provided constraint. However, this method does not verify that existing
170    * elements satisfy the constraint.
171    *
172    * <p>If {@code list} implements {@link RandomAccess}, so will the returned
173    * list. The returned list is not serializable.
174    *
175    * @param list the list to constrain
176    * @param constraint the constraint that validates added elements
177    * @return a constrained view of the list
178    */
179   public static <E> List<E> constrainedList(
180       List<E> list, Constraint<? super E> constraint) {
181     return (list instanceof RandomAccess)
182         ? new ConstrainedRandomAccessList<E>(list, constraint)
183         : new ConstrainedList<E>(list, constraint);
184   }
185 
186   /** @see Constraints#constrainedList */
187   @GwtCompatible
188   private static class ConstrainedList<E> extends ForwardingList<E> {
189     final List<E> delegate;
190     final Constraint<? super E> constraint;
191 
192     ConstrainedList(List<E> delegate, Constraint<? super E> constraint) {
193       this.delegate = checkNotNull(delegate);
194       this.constraint = checkNotNull(constraint);
195     }
196     @Override protected List<E> delegate() {
197       return delegate;
198     }
199 
200     @Override public boolean add(E element) {
201       constraint.checkElement(element);
202       return delegate.add(element);
203     }
204     @Override public void add(int index, E element) {
205       constraint.checkElement(element);
206       delegate.add(index, element);
207     }
208     @Override public boolean addAll(Collection<? extends E> elements) {
209       return delegate.addAll(checkElements(elements, constraint));
210     }
211     @Override public boolean addAll(int index, Collection<? extends E> elements)
212     {
213       return delegate.addAll(index, checkElements(elements, constraint));
214     }
215     @Override public ListIterator<E> listIterator() {
216       return constrainedListIterator(delegate.listIterator(), constraint);
217     }
218     @Override public ListIterator<E> listIterator(int index) {
219       return constrainedListIterator(delegate.listIterator(index), constraint);
220     }
221     @Override public E set(int index, E element) {
222       constraint.checkElement(element);
223       return delegate.set(index, element);
224     }
225     @Override public List<E> subList(int fromIndex, int toIndex) {
226       return constrainedList(
227           delegate.subList(fromIndex, toIndex), constraint);
228     }
229   }
230 
231   /** @see Constraints#constrainedList */
232   static class ConstrainedRandomAccessList<E> extends ConstrainedList<E>
233       implements RandomAccess {
234     ConstrainedRandomAccessList(
235         List<E> delegate, Constraint<? super E> constraint) {
236       super(delegate, constraint);
237     }
238   }
239 
240   /**
241    * Returns a constrained view of the specified list iterator, using the
242    * specified constraint. Any operations that would add new elements to the
243    * underlying list will be verified by the constraint.
244    *
245    * @param listIterator the iterator for which to return a constrained view
246    * @param constraint the constraint for elements in the list
247    * @return a constrained view of the specified iterator
248    */
249   private static <E> ListIterator<E> constrainedListIterator(
250       ListIterator<E> listIterator, Constraint<? super E> constraint) {
251     return new ConstrainedListIterator<E>(listIterator, constraint);
252   }
253 
254   /** @see Constraints#constrainedListIterator */
255   static class ConstrainedListIterator<E> extends ForwardingListIterator<E> {
256     private final ListIterator<E> delegate;
257     private final Constraint<? super E> constraint;
258 
259     public ConstrainedListIterator(
260         ListIterator<E> delegate, Constraint<? super E> constraint) {
261       this.delegate = delegate;
262       this.constraint = constraint;
263     }
264     @Override protected ListIterator<E> delegate() {
265       return delegate;
266     }
267 
268     @Override public void add(E element) {
269       constraint.checkElement(element);
270       delegate.add(element);
271     }
272     @Override public void set(E element) {
273       constraint.checkElement(element);
274       delegate.set(element);
275     }
276   }
277 
278   static <E> Collection<E> constrainedTypePreservingCollection(
279       Collection<E> collection, Constraint<E> constraint) {
280     if (collection instanceof SortedSet) {
281       return constrainedSortedSet((SortedSet<E>) collection, constraint);
282     } else if (collection instanceof Set) {
283       return constrainedSet((Set<E>) collection, constraint);
284     } else if (collection instanceof List) {
285       return constrainedList((List<E>) collection, constraint);
286     } else {
287       return constrainedCollection(collection, constraint);
288     }
289   }
290 
291   /*
292    * TODO(kevinb): For better performance, avoid making a copy of the elements
293    * by having addAll() call add() repeatedly instead.
294    */
295 
296   private static <E> Collection<E> checkElements(
297       Collection<E> elements, Constraint<? super E> constraint) {
298     Collection<E> copy = Lists.newArrayList(elements);
299     for (E element : copy) {
300       constraint.checkElement(element);
301     }
302     return copy;
303   }
304 }